package com.ftg.learn.chapter14;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.pool.DruidPooledConnection;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Properties;

/**
 * 性能优化...[性能]
 * <p>
 * 1 : jdbc 连接 mysql 慢的原因是什么?
 * 1.1 tcp mysql认证 sql执行 mysql关闭 tcp关闭
 * java 启动 长一些
 * 1.2 map.put("connection1",DriverManager.getConnection(。。。))
 * 1.3 map.get("connection")
 * 池化 ： 资源 Connection 使用者 jdbc-sql  池 (map[集合|即容器])
 * :  池 （初始化连接数5|最大连接数20|扩容系数5|等待时长）
 * 连接池 : c3p0 dbcp druid(...)
 * 两个中间件 fastjson2 druid
 * 作业调度池  ： job
 * 线程池 ：
 * 类池 ： 类的实例
 *
 * 1 :  maven 打包 ：
 * 1.1
 * 2 ： 代码与配置分离
 * @author kangjx
 * @version 1.0
 * @date 2023/3/8 上午8:14
 */
public class LearnJdbc08 {

    /**
     * 静态源，保证Druid实例化一次
     */
    private final static DruidDataSource DATASOURCE;

    static {

        Properties p = new Properties();
        ClassLoader classLoader = LearnJdbc08.class.getClassLoader();
        InputStream reader = classLoader.getResourceAsStream("jdbc.properties");
        try {
            p.load(reader);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        DATASOURCE = new DruidDataSource();
        DATASOURCE.setUrl(p.getProperty("jdbc.mysql.url"));
        DATASOURCE.setUsername(p.getProperty("jdbc.mysql.username"));
        DATASOURCE.setPassword(p.getProperty("jdbc.mysql.password"));
        //初始大小
        DATASOURCE.setInitialSize(2);
        //最大连接数
        DATASOURCE.setMaxActive(4);
        //最小活动连接数
        DATASOURCE.setMinIdle(4);
        //查询时长
        DATASOURCE.setQueryTimeout(0);
        //最大等待时间
        DATASOURCE.setMaxWait(6000);

        //配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
        DATASOURCE.setTimeBetweenEvictionRunsMillis(60);
        //默认为false，表示超时不回收，如果为true表示超时回收
        DATASOURCE.setRemoveAbandoned(true);
        //4 秒
        DATASOURCE.setRemoveAbandonedTimeout(4);
        DATASOURCE.setDriverClassName("com.mysql.cj.jdbc.Driver");
    }

    /**
     * 通过druid来获得 Connection
     *
     * @return Connection
     */
    public Connection getDruidConnection() {
        try {
            DruidPooledConnection connection = DATASOURCE.getConnection();
            return connection.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 执行查询 sql
     * @param c   返回的对象类型
     * @param sql  语句
     * @param para 参数
     * @return   List<T>
     * @param <T> 对象类型
     */
    @SuppressWarnings("all")
    public <T> List<T> queryFoPojo1(Class<T> c, String sql, Object... para) {
        List<T> list = new ArrayList<>();
        try {
            Connection conn = this.getDruidConnection();
            PreparedStatement pstm = conn.prepareStatement(sql);
            sqlWithParameterVal(pstm,para);

            ResultSet rs = pstm.executeQuery();
            ResultSetMetaData metaData = rs.getMetaData();

            while (rs.next()) {

                Object obj = c.newInstance();
                for (int i = 0; i < metaData.getColumnCount(); i++) {
                    PropertyDescriptor pd = new PropertyDescriptor(metaData.getColumnName(i + 1), c);
                    Method setMethod = pd.getWriteMethod();
                    //switch 判断字段类型 用来做精准存储
                    setMethod.invoke(obj, rs.getObject(i + 1));
                }
                list.add((T) obj);
            }

        } catch (InstantiationException | IllegalAccessException | SQLException | IntrospectionException |
                 InvocationTargetException e) {
            throw new RuntimeException(e);
        }
        return list;
    }

    /**
     * 为当前sql里面的?赋值的方法
     * @param pstm  预编译
     * @param para  参数
     * @throws SQLException
     */
    private void sqlWithParameterVal(PreparedStatement pstm,Object...para) throws SQLException {
        if (Objects.nonNull(para)) {
            for (int i = 0; i < para.length; i++) {
                pstm.setObject(i + 1, para[i]);
            }
        }
    }

    /**
     * 完成添加、修改、删除sql语句的方法
     * @param sql  sql
     * @param para para
     * @return int 响应行
     */
    public int doDml(String sql,Object...para){
        try {
            Connection conn = this.getDruidConnection();
            PreparedStatement pstm = conn.prepareStatement(sql);
            sqlWithParameterVal(pstm,para);
            return pstm.executeUpdate();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    public static void main(String[] args) {
        LearnJdbc08 l = new LearnJdbc08();
        l.queryFoPojo1(StudentPojo.class,"select * from student").forEach(System.out::println);
    }


}
